package com.aizuda.boot.modules.business.log.aspect;

import cn.hutool.core.util.ObjectUtil;
import com.aizuda.boot.modules.business.contract.mapper.ContractFieldValueMapper;
import com.aizuda.boot.modules.common.constant.enums.SystemFieldsEnum;
import com.alibaba.fastjson.JSONObject;
import com.aizuda.boot.modules.business.contract.dto.UserInfo;
import com.aizuda.boot.modules.business.contract.util.AuthUtil;
import com.aizuda.boot.modules.business.log.annotation.Logging;
import com.aizuda.boot.modules.business.log.domain.request.LogSaveDTO;
import com.aizuda.boot.modules.common.constant.enums.LogTypeEnum;
import com.aizuda.boot.modules.business.log.service.LogService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Date;

/**
 * @Author sangyirong
 * @ClassName SaveLog
 * @Version 1.0
 * @Date 2025/6/6 18:35
 */
@Component
@Aspect
@Order(-20)
@Slf4j
public class SaveLog {

    @Resource
    private LogService logService;

    @Resource
    private ContractFieldValueMapper contractFieldValueMapper;

    @Around("@annotation(com.aizuda.boot.modules.business.log.annotation.Logging)")
    public Object saveLog(ProceedingJoinPoint joinPoint) throws Throwable {
        UserInfo currentUser = AuthUtil.getCurrentUser();
        if (ObjectUtil.isNull(currentUser) || currentUser.getEmployeeId() == null) {
            log.warn("用户未登录，跳过日志记录");
            return joinPoint.proceed();
        }

        // 获取方法注解信息
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        Logging loggingAnnotation = method.getAnnotation(Logging.class);

        if (loggingAnnotation == null) {
            log.warn("未找到@Logging注解，跳过日志记录");
            return joinPoint.proceed();
        }

        // 获取请求信息
        String sourceIp = getClientIp();

        // 创建日志对象
        LogSaveDTO logSaveDTO = new LogSaveDTO();

        // 获取日志类型
        LogTypeEnum logType = loggingAnnotation.logType();

        // 构建操作内容 - 从方法参数中提取关键信息
        String operationContent = buildOperationContent(logType, joinPoint);

        // 提取合同ID（如果存在）
        Integer contractId = extractContractId(joinPoint);

        // 赋值基本信息
        logSaveDTO.setEventType(logType);
        logSaveDTO.setOperationContent(operationContent);
        logSaveDTO.setOperatorId(currentUser.getEmployeeId());
        logSaveDTO.setOperationTime(new Date());
        logSaveDTO.setSourceIp(sourceIp);
        logSaveDTO.setContractId(contractId); // 保存合同ID

        // 执行目标方法
        Object result = null;
        String errorMessage = null;

        try {
            result = joinPoint.proceed();
            logSaveDTO.setResult(true);
        } catch (Throwable e) {
            errorMessage = e.getMessage();
            logSaveDTO.setResult(false);
            logSaveDTO.setErrorMessage(errorMessage);
            throw e; // 重新抛出异常
        } finally {
            // 保存日志（异步处理，不影响主业务）
            try {
                logService.saveLog(logSaveDTO);
                log.debug("操作日志保存成功: {}", operationContent);
            } catch (Exception e) {
                log.error("保存操作日志失败", e);
                // 日志保存失败不影响主业务
            }
        }

        return result;
    }

    /**
     * 构建操作内容，格式为"合同：合同id"或者"文件：文件名"
     */
    private String buildOperationContent(LogTypeEnum logType, ProceedingJoinPoint joinPoint) {
        try {
            // 从方法参数中提取关键信息
            String keyInfo = extractKeyInfo(joinPoint);

            if (StringUtils.hasText(keyInfo)) {
                return String.format("%s：%s", logType.getMsg(), keyInfo);
            } else {
                return logType.getMsg();
            }
        } catch (Exception e) {
            log.warn("构建操作内容失败，使用默认格式", e);
            return logType.getMsg();
        }
    }

    /**
     * 从方法参数中提取关键信息（合同ID或文件名）
     * 优先级：合同ID > 文件信息
     */
    private String extractKeyInfo(ProceedingJoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        if (args == null || args.length == 0) {
            return null;
        }

        // 第一步：优先查找合同ID
        Integer contractId = extractContractId(joinPoint);
        if (contractId != null) {
            return "合同【" + contractFieldValueMapper.getFieldValueByCode(contractId, SystemFieldsEnum.CONTRACT_NAME.getCode()) + "】。";
        }

        // 第二步：如果没有合同ID，查找文件信息
        return extractFileInfo(args);
    }

    /**
     * 提取文件信息
     */
    private String extractFileInfo(Object[] args) {
        for (Object arg : args) {
            if (arg == null) {
                continue;
            }

            // 1. 检查是否是MultipartFile类型（单个文件上传）
            if (arg instanceof MultipartFile) {
                MultipartFile file = (MultipartFile) arg;
                String fileName = file.getOriginalFilename();
                if (StringUtils.hasText(fileName)) {
                    return "文件" + fileName;
                }
            }

            // 2. 检查是否是MultipartFile数组（多文件上传）
            if (arg instanceof MultipartFile[]) {
                MultipartFile[] files = (MultipartFile[]) arg;
                if (files.length > 0 && files[0] != null) {
                    String fileName = files[0].getOriginalFilename();
                    if (StringUtils.hasText(fileName)) {
                        return "文件" + fileName + (files.length > 1 ? "等" + files.length + "个文件" : "");
                    }
                }
            }

            // 3. 从DTO对象中提取文件信息
            if (isComplexObject(arg)) {
                String fileInfo = extractFileInfoFromDto(arg);
                if (StringUtils.hasText(fileInfo)) {
                    return fileInfo;
                }
            }
        }

        return null;
    }

    /**
     * 从DTO对象中提取文件信息
     */
    private String extractFileInfoFromDto(Object dto) {
        try {
            JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(dto));

            // 检查文件名相关字段
            String[] fileFields = {"fileName", "filename", "originalFilename", "name"};
            for (String field : fileFields) {
                Object value = jsonObject.get(field);
                if (value != null && StringUtils.hasText(value.toString())) {
                    return "文件" + value.toString();
                }
            }

            // 检查UUID字段（文档相关）
            Object uuid = jsonObject.get("uuid");
            if (uuid != null && StringUtils.hasText(uuid.toString())) {
                return "文档" + uuid.toString();
            }

        } catch (Exception e) {
            log.debug("从DTO提取文件信息失败", e);
        }
        return null;
    }

    /**
     * 获取客户端IP地址
     */
    private String getClientIp() {
        try {
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes sra = (ServletRequestAttributes) requestAttributes;
            HttpServletRequest request = sra.getRequest();

            // 尝试获取“x-forwarded-for”头，这是最常用的代理头字段。
            String ip = request.getHeader("x-forwarded-for");
            // 检查“x-forwarded-for”头是否有效，并提取第一个IP地址。
            if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
                // 多次反向代理后会有多个ip值，第一个ip才是真实ip
                if (ip.contains(",")) {
                    ip = ip.split(",")[0];
                }
            }
            // 如果“x-forwarded-for”头无效，尝试其他不那么常见的代理头字段。
            if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
            }
            if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
            if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("X-Real-IP");
            }
            // 如果所有代理头字段都无效，回退到使用请求的远程地址作为客户端IP。
            if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
            // 返回获取到的IP地址，无论它是通过代理头还是直接从请求中获取。
            return ip;
        } catch (Exception e) {
            log.warn("获取客户端IP失败", e);
        }
        return "unknown";
    }

    /**
     * 提取合同ID - 优先级处理
     */
    private Integer extractContractId(ProceedingJoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        if (args == null || args.length == 0) {
            return null;
        }

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        Parameter[] parameters = method.getParameters();
        String methodName = joinPoint.getSignature().getName().toLowerCase();

        // 第一优先级：检查参数名包含contractId的参数
        for (int i = 0; i < args.length && i < parameters.length; i++) {
            if (args[i] == null) {
                continue;
            }
            String paramName = parameters[i].getName().toLowerCase();
            if (paramName.contains("contractid")) {
                return convertToInteger(args[i], "参数名contractId");
            }
        }

        // 第二优先级：方法名包含contract的Integer/Long参数（路径变量）
        if (methodName.contains("contract")) {
            for (Object arg : args) {
                if (arg instanceof Integer || arg instanceof Long) {
                    return convertToInteger(arg, "路径变量");
                }
            }
        }

        // 第三优先级：从DTO对象中提取合同ID
        for (Object arg : args) {
            if (isComplexObject(arg)) {
                Integer contractIdFromDto = extractContractIdFromDto(arg);
                if (contractIdFromDto != null) {
                    return contractIdFromDto;
                }
            }
        }

        return null;
    }

    /**
     * 转换对象为Integer，带日志
     */
    private Integer convertToInteger(Object value, String source) {
        try {
            return Integer.valueOf(value.toString());
        } catch (NumberFormatException e) {
            log.debug("{}的值无法转换为Integer: {}", source, value);
            return null;
        }
    }

    /**
     * 从DTO对象中提取合同ID
     */
    private Integer extractContractIdFromDto(Object dto) {
        try {
            JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(dto));

            // 按优先级检查合同ID相关字段
            String[] contractFields = {"contractId", "id", "contractIds"};
            for (String field : contractFields) {
                Object value = jsonObject.get(field);
                if (value != null) {
                    // 处理单个合同ID
                    if (!(value instanceof java.util.List)) {
                        return convertToInteger(value, "DTO字段" + field);
                    }
                    // 处理合同ID列表，取第一个
                    else {
                        java.util.List<?> list = (java.util.List<?>) value;
                        if (!list.isEmpty()) {
                            return convertToInteger(list.get(0), "DTO字段" + field + "列表");
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.debug("从DTO提取合同ID失败", e);
        }
        return null;
    }

    /**
     * 判断是否是复杂对象（非基本类型和常用类型）
     */
    private boolean isComplexObject(Object obj) {
        if (obj == null) {
            return false;
        }

        Class<?> clazz = obj.getClass();

        // 排除基本类型、包装类型、字符串等
        if (clazz.isPrimitive() ||
                clazz.equals(String.class) ||
                Number.class.isAssignableFrom(clazz) ||
                clazz.equals(Boolean.class) ||
                clazz.equals(Character.class) ||
                clazz.getName().startsWith("java.") ||
                clazz.getName().startsWith("javax.")) {
            return false;
        }

        return true;
    }
}
